import {RECORDER_INTERFACE, RECORDER_TYPE} from "./recorder_interface";
import {WebMRecorder} from "./webm_recorder";
import {WorkletRecorder} from "./worklet_recorder";
import {ScriptRecorder} from "./script_recorder";

enum WLRecorder_State {
    RECORDER_OPENING_STATE,
    RECORDER_OPENED_STATE,
    RECORDER_CLOSING_STATE,
    RECORDER_CLOSED_STATE,
    RECORDER_STARTING_STATE,
    RECORDER_STARTED_STATE,
    RECORDER_STOPPING_STATE,
}

class WLRecorder {
    private micStream?: any;
    private scope?: any;
    private recorder?: RECORDER_INTERFACE;
    private recorderDataCb?: (data: Int16Array) => void;
    private onError?: (error: string) => void;
    private validRecordType = RECORDER_TYPE.AUTO_DETECT;
    private recorderState: WLRecorder_State = WLRecorder_State.RECORDER_CLOSED_STATE;
    private audioContext: any = null;

    constructor(private sampleRate: number) {
        this.scope = navigator.mediaDevices || {};

        if (!this.scope.getUserMedia) {
            this.scope = navigator;
            // 判断到getUserMedia有定义，则不作为，否则赋值有效的
            this.scope.getUserMedia ||
            (this.scope.getUserMedia = this.scope.webkitGetUserMedia ||
                this.scope.mozGetUserMedia || this.scope.msGetUserMedia);
        }
    }

    private isStreamOpen(): boolean {
        if (this.micStream) {
            let tracks = this.micStream.getTracks && this.micStream.getTracks() || this.micStream.audioTracks || [];
            let track = tracks[0];
            if (track) {
                let state = track.readyState;
                return state == "live" || state == track.LIVE;
            }
        }

        return false;
    }

    public getRecorderDataSamplerate(): number {
        if (this.recorder) {
            return this.recorder.getSampleRate();
        }

        return 0;
    }

    public getRecorderType(): number {
        if (this.recorder) {
            return this.recorder.getRecorderType();
        }

        return -1;
    }

    public setRecorderDataCb(cb: (data: Int16Array) => void) {
        this.recorderDataCb = cb;
    }

    public setErrorCb(cb: (error: string) => void) {
        this.onError = cb;
    }

    private onReceivePCMData(f32Arr: Float32Array): void {
        let i16Buffer: Int16Array|null = new Int16Array(f32Arr.map(v => {
            return Math.ceil(v * 32768);
        }));

        if (this.recorderDataCb) {
            this.recorderDataCb(i16Buffer);
        }

        i16Buffer = null;
    }

    private async tryConnectByWebM(): Promise<boolean> {
        const webM = new WebMRecorder(this.micStream, 48000);
        if (webM.isSupport()) {
            webM.onError(async (e: any) => {
                console.log("WEBM 录音器出错", e);
                try {
                    await this.tryConnectByWorklet();
                    this.validRecordType = this.recorder!.getRecorderType();
                    if (this.recorderState == WLRecorder_State.RECORDER_STARTED_STATE) {
                        await this.start();
                    }
                }catch (e) {
                    try {
                        await this.tryConnectByScript();
                        this.validRecordType = this.recorder!.getRecorderType();
                        if (this.recorderState == WLRecorder_State.RECORDER_STARTED_STATE) {
                            await this.start();
                        }
                    }catch (e2) {
                        console.log("出错了", e2);
                        if (this.onError) {
                            this.onError("录音出错" + JSON.stringify(e2));
                        }
                    }
                }

            });

            webM.onDataReceive(this.onReceivePCMData.bind(this));
            await webM.connect();
            this.recorder = webM;
            console.log("WEBM 录音器已连接");
            return true;
        }else {
            console.log("WEBM 录音器不支持");
        }

        return Promise.reject("录音器支持");
    }

    private async tryConnectByWorklet(): Promise<boolean> {
        console.log("尝试连接 worklet 录音器");
        const worklet = new WorkletRecorder(this.audioContext, this.micStream);
        if (worklet.isSupport()) {
            worklet.onDataReceive(this.onReceivePCMData.bind(this));
            await worklet.connect();
            this.recorder = worklet;
            console.log("WORKLET 录音器已连接");
            return true;
        }

        return Promise.reject("录音器不支持");
    }

    private async tryConnectByScript(): Promise<boolean> {
        console.log("尝试连接 script 录音器");
        const script = new ScriptRecorder(this.audioContext, this.micStream);
        if (script.isSupport()) {
            script.onDataReceive(this.onReceivePCMData.bind(this));
            await script.connect();
            this.recorder = script;
            console.log("SCRIPT 录音器已连接");
            return true;
        }else {
            console.log("SCRIPT 录音器不支持");
        }

        return Promise.reject("录音器不支持");
    }

    public async open(): Promise<boolean> {
        let AC = window.AudioContext;
        if (!AC) {
            // @ts-ignore
            AC = window.webkitAudioContext;
        }
        this.audioContext = new AC();

        return new Promise<boolean>((resolve, reject) => {
            const succ = ((value: any) => {
                console.log("获取mic成功, recorder Type", this.validRecordType)
                this.recorderState = WLRecorder_State.RECORDER_OPENING_STATE;
                setTimeout(async () => {
                    this.micStream = value;
                    if (this.isStreamOpen()) {
                        try {
                            if (this.validRecordType == RECORDER_TYPE.AUTO_DETECT || this.validRecordType == RECORDER_TYPE.WEBM_RECORDER) {
                                await this.tryConnectByWebM();
                                console.log("连接WebM")
                                this.validRecordType = this.recorder!.getRecorderType();
                            }else {
                                throw new Error("not using");
                            }
                        }catch (e) {
                            console.log("连接WebM失败，尝试Worklet", e);
                            try {
                                if (this.validRecordType == RECORDER_TYPE.AUTO_DETECT || this.validRecordType == RECORDER_TYPE.WORKLET_RECORDER) {
                                    await this.tryConnectByWorklet();
                                    this.validRecordType = this.recorder!.getRecorderType();
                                }else {
                                    throw new Error("not using");
                                }
                            }catch (e2) {
                                console.log("连接Worklet失败，尝试Script", e2);
                                try {
                                    await this.tryConnectByScript();
                                    this.validRecordType = this.recorder!.getRecorderType();
                                }catch (e3) {
                                    this.recorderState = WLRecorder_State.RECORDER_CLOSED_STATE;
                                    this.validRecordType = RECORDER_TYPE.AUTO_DETECT;
                                    reject("录音器均不支持");
                                    return;
                                }
                            }
                        }
                        this.recorderState = WLRecorder_State.RECORDER_OPENED_STATE;
                        resolve(true);
                    }else {
                        this.recorderState = WLRecorder_State.RECORDER_CLOSED_STATE;
                        reject("录音流无法打开");
                    }
                }, 100)
            });

            let promise: any = undefined;
            try {
                promise = this.scope.getUserMedia({
                    audio: {
                        noiseSuppression: false,
                        echoCancellation: false,
                        autoGainControl: true,
                        sampleRate: this.sampleRate
                    }
                }, succ, reject);
            }catch (e) {
                console.log("获取UserMedia出错", e);
                promise = this.scope.getUserMedia({audio: true}, succ, reject);
            }

            if (promise && promise.then) {
                promise.then(succ).catch(reject);
            }
        });
    }

    public async start(): Promise<boolean> {
        console.log("start..............", this.recorder);
        if (this.recorder) {
            this.recorderState = WLRecorder_State.RECORDER_STARTING_STATE;
            try {
                await this.recorder.start();
                this.recorderState = WLRecorder_State.RECORDER_STARTED_STATE;
                return true;
            }catch (e) {
                this.recorderState = WLRecorder_State.RECORDER_OPENED_STATE;
                return Promise.reject("录音器打开失败" + JSON.stringify(e));
            }
        }

        return Promise.reject("没有适合的录音器")
    }

    public async stop(): Promise<boolean> {
        if (this.recorder) {
            this.recorderState = WLRecorder_State.RECORDER_STOPPING_STATE;
            try {
                await this.recorder.stop();
                return true;
            }catch (e) {
                return Promise.reject("停止录音器失败" + JSON.stringify(e));
            }finally {
                this.recorderState = WLRecorder_State.RECORDER_OPENED_STATE;
            }
        }

        return Promise.reject("没有适合的录音器");
    }

    public async close(): Promise<boolean> {
        if (this.audioContext) {
            this.audioContext.close();
            this.audioContext = null;
        }

        try {
            this.recorderState = WLRecorder_State.RECORDER_CLOSING_STATE;
            if (this.recorder) {
                await this.recorder.disconnect();
                this.recorder = undefined;
            }

            if (this.micStream) {
                let tracks = this.micStream.getTracks && this.micStream.getTracks() || this.micStream.audioTracks;
                for (let i = 0; i < tracks.length; i++) {
                    let track = tracks[0];
                    track.stop && track.stop();
                }

                this.micStream.stop && this.micStream.stop();
                this.micStream = undefined;
            }

            return true;
        }catch (e) {
            return Promise.reject("关闭录音器失败" + JSON.stringify(e));
        }finally {
            this.recorderState = WLRecorder_State.RECORDER_CLOSED_STATE;
        }

        return true;
    }
}

export {WLRecorder}
